//	Project:	HexTower
//	File:		Direct3D_Manager.cpp
//	Author:		Mustafa Aldoori
//	Purpose:	Implementation for the Direct3D Manager.

#include "Direct3D_Manager.h"

// Initialize the static object.
Direct3D_Manager Direct3D_Manager::d3d_Instance;

// Default Constructor
Direct3D_Manager::Direct3D_Manager( void )
{
	d3d_Direct3DObject = nullptr;
	d3d_Direct3DDevice = nullptr;
	d3d_Sprite = nullptr;
	d3d_Font = nullptr;
	d3d_Line = nullptr;
	memset( &d3d_PresentParams, 0, sizeof( D3DPRESENT_PARAMETERS ) );
}

// Destructor
Direct3D_Manager::~Direct3D_Manager( void ) { }

// Get Instance
Direct3D_Manager* Direct3D_Manager::GetInstance( void )
{
	return &d3d_Instance;
}

// Initialize the Direct3D_Manager
bool Direct3D_Manager::InitDirect3D(HWND hWnd, int nScreenWidth, int nScreenHeight, bool bIsWindowed, bool bVsync)
{
	// Make sure the hWnd is valid.
	if (!hWnd) return false;

	// Set the handle to the window.
	d3d_hWnd = hWnd;

	// Create the Direct3D9 Object.
	d3d_Direct3DObject = Direct3DCreate9(D3D_SDK_VERSION);

	// Make sure the object is valid.
	if (!d3d_Direct3DObject) DXError( _T( "Failed to Create D3D Object" ) )

	// Setup the parameters for using Direct3D.
	d3d_PresentParams.BackBufferWidth				= nScreenWidth;
	d3d_PresentParams.BackBufferHeight				= nScreenHeight;
	d3d_PresentParams.BackBufferFormat				= (bIsWindowed) ? D3DFMT_UNKNOWN : D3DFMT_R5G6B5;
	d3d_PresentParams.BackBufferCount				= 1;
	d3d_PresentParams.MultiSampleType				= D3DMULTISAMPLE_NONE;
	d3d_PresentParams.MultiSampleQuality			= 0;
	d3d_PresentParams.SwapEffect					= D3DSWAPEFFECT_COPY;
	d3d_PresentParams.hDeviceWindow					= hWnd;
	d3d_PresentParams.Windowed						= bIsWindowed;
	d3d_PresentParams.EnableAutoDepthStencil		= false;
	d3d_PresentParams.Flags							= D3DPRESENTFLAG_LOCKABLE_BACKBUFFER;
	d3d_PresentParams.FullScreen_RefreshRateInHz	= D3DPRESENT_RATE_DEFAULT;
	d3d_PresentParams.PresentationInterval			= ( bVsync ) ? D3DPRESENT_INTERVAL_DEFAULT : D3DPRESENT_INTERVAL_IMMEDIATE;

	// Create the Direct3D Device.
	if( FAILED( d3d_Direct3DObject->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
		D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED,
		&d3d_PresentParams, &d3d_Direct3DDevice ) ) )
		DXError( _T( "Failed to Create D3D Device" ) )

	// Create Sprite Object.
	if( FAILED( D3DXCreateSprite( d3d_Direct3DDevice, &d3d_Sprite ) ) )
		DXError( _T( "Failed to Create the Sprite object" ) )

	// Create the Line Object.
	if( FAILED(D3DXCreateLine( d3d_Direct3DDevice, &d3d_Line ) ) )
		DXError( _T( "Failed to Create the Line Object" ) )

	// Setup Line parameters.
	d3d_Line->SetAntialias( TRUE );
	d3d_Line->SetWidth( 3.0f );

	//	Return success.
	return true;
}

void Direct3D_Manager::ShutdownDirect3D( void )
{
	Safe_Release(d3d_Font);
	Safe_Release(d3d_Line);
	Safe_Release(d3d_Sprite);
	Safe_Release(d3d_Direct3DDevice);
	Safe_Release(d3d_Direct3DObject);
}

void Direct3D_Manager::Clear( unsigned char ucRed, unsigned char ucGreen, unsigned char ucBlue )
{
	d3d_Direct3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( ucRed, ucGreen, ucBlue ), 1.0f, 0 );

	// Check for lost device (could happen from an ALT+TAB or ALT+ENTER).
	if( d3d_Direct3DDevice->TestCooperativeLevel() == D3DERR_DEVICENOTRESET )
	{
		d3d_Line->OnLostDevice();
		d3d_Sprite->OnLostDevice();
		d3d_Direct3DDevice->Reset( &d3d_PresentParams );
		d3d_Sprite->OnResetDevice();
		d3d_Line->OnResetDevice();
	}
}

bool Direct3D_Manager::DeviceBegin( void )
{
	if( FAILED( d3d_Direct3DDevice->BeginScene() ) )
		DXError( _T( "Failed to begin device scene." ) )

	return true;
}

bool Direct3D_Manager::DeviceEnd( void )
{
	if( FAILED( d3d_Direct3DDevice->EndScene() ) )
		DXError( _T( "Failed to end device scene." ) )

	return true;
}

bool Direct3D_Manager::SpriteBegin( void )
{
	if ( FAILED( d3d_Sprite->Begin(D3DXSPRITE_ALPHABLEND ) ) )
		DXError( _T( "Failed to begin sprite scene." ) )

	return true;
}

bool Direct3D_Manager::SpriteEnd( void )
{
	if( FAILED( d3d_Sprite->End() ) )
		DXError( _T( "Failed to end sprite scene." ) )

	return true;
}

bool Direct3D_Manager::LineBegin( void )
{
	if (FAILED(d3d_Line->Begin()))
		DXError( _T( "Failed to begin line scene." ) )

	return true;
}

bool Direct3D_Manager::LineEnd( void )
{
	if( FAILED( d3d_Line->End() ) )
		DXError( _T( "Failed to end line scene." ) )

	return true;
}

void Direct3D_Manager::Present( void )
{
	d3d_Direct3DDevice->Present( nullptr, nullptr, nullptr, nullptr );
}

void Direct3D_Manager::ChangeDisplayParam( int nWidth, int nHeight, bool bWindowed )
{
	// Set the new Presentation Parameters.
	d3d_PresentParams.BackBufferFormat	= ( bWindowed ) ? D3DFMT_UNKNOWN : D3DFMT_R5G6B5;
	d3d_PresentParams.Windowed			= bWindowed;
	d3d_PresentParams.BackBufferWidth	= nWidth;
	d3d_PresentParams.BackBufferHeight	= nHeight;

	// Reset the device.
	d3d_Line->OnLostDevice();
	d3d_Sprite->OnLostDevice();
	d3d_Direct3DDevice->Reset( &d3d_PresentParams );
	d3d_Sprite->OnResetDevice();
	d3d_Line->OnResetDevice();

	// Setup window style flags
	DWORD dwWindowStyleFlags = WS_VISIBLE;

	HWND top;
	if( bWindowed )
	{
		dwWindowStyleFlags |= WS_OVERLAPPEDWINDOW;
		ShowCursor( TRUE ); // show the mouse cursor
		top = HWND_NOTOPMOST;
	}
	else
	{
		dwWindowStyleFlags |= WS_POPUP;
		ShowCursor( FALSE ); // hide the mouse cursor
		top = HWND_TOP;
	}

	SetWindowLong( d3d_hWnd, GWL_STYLE, dwWindowStyleFlags );

	//	Set the window to the middle of the screen.
	if( bWindowed )
	{
		// Setup the desired client area size
		RECT rWindow;
		rWindow.left	= 0;
		rWindow.top		= 0;
		rWindow.right	= nWidth;
		rWindow.bottom	= nHeight;

		// Get the dimensions of a window that will have a client rect that
		// will really be the resolution we're looking for.
		AdjustWindowRectEx(&rWindow, 
							dwWindowStyleFlags,
							FALSE, 
							WS_EX_APPWINDOW);
		
		// Calculate the width/height of that window's dimensions
		int windowWidth		= rWindow.right - rWindow.left;
		int windowHeight	= rWindow.bottom - rWindow.top;

		SetWindowPos(d3d_hWnd, top,	( GetSystemMetrics(SM_CXSCREEN)>>1 ) - ( windowWidth>>1 ),
										( GetSystemMetrics(SM_CYSCREEN)>>1 ) - ( windowHeight>>1 ),
										windowWidth, windowHeight, SWP_SHOWWINDOW );
		HRGN hrgn = CreateRectRgn( 10, 30, 800, 630 );
		SetWindowRgn( d3d_hWnd, hrgn, TRUE );

	}
	else
	{
		// Let windows know the window has changed.
		SetWindowPos( d3d_hWnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE );
	}
}

void Direct3D_Manager::DrawRect(RECT rRt, unsigned char ucRed, unsigned char ucGreen, unsigned char ucBlue)
{
	D3DRECT d3dRect;
	d3dRect.x1 = rRt.left;
	d3dRect.y1 = rRt.top;
	d3dRect.x2 = rRt.right;
	d3dRect.y2 = rRt.bottom;

	d3d_Direct3DDevice->Clear( 1, &d3dRect, D3DCLEAR_TARGET, D3DCOLOR_XRGB(ucRed, ucGreen, ucBlue), 1.0f, 0 );
}

void Direct3D_Manager::DrawLine( int nX1, int nY1, int nX2, int nY2, unsigned char ucRed, unsigned char ucGreen, unsigned char ucBlue)
{
	D3DXVECTOR2 verts[ 2 ];

	verts[0].x = (float)nX1;
	verts[0].y = (float)nY1;
	verts[1].x = (float)nX2;
	verts[1].y = (float)nY2;

	d3d_Line->Draw( verts, 2, D3DCOLOR_XRGB( ucRed, ucGreen, ucBlue ) );
}

void Direct3D_Manager::DrawText( TCHAR *lpzText, int nX, int nY, unsigned char ucRed, unsigned char ucGreen, unsigned char ucBlue)
{
	// Pointer to the Back Buffer.
	LPDIRECT3DSURFACE9 d3dBackBuffer = 0;

	// Get the Back Buffer from the Device.
	d3d_Direct3DDevice->GetBackBuffer( 0, 0, D3DBACKBUFFER_TYPE_MONO, &d3dBackBuffer );

	// Get a Device Context.
	HDC hdc = 0;
	d3dBackBuffer->GetDC( &hdc );

	// Print the string out to the screen.
	SetTextColor( hdc, RGB( ucRed, ucGreen, ucBlue ) );
	SetBkMode( hdc, TRANSPARENT );
	TextOut( hdc, nX, nY, lpzText, (int)_tcslen( lpzText ) );

	// Release the Device Context.
	d3dBackBuffer->ReleaseDC( hdc );
	Safe_Release( d3dBackBuffer );
}
